package com.lin.server.ws;



import com.lin.entity.key.WsTopicKey;
import com.lin.utils.CommonUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * ws服务端
 * 客户端发起ws连接：ws://ip:port/webSocket/topic
 */
@Component
@ServerEndpoint(value = "/webSocket", configurator = WebSocketServer.SpringConfigurator.class)
@Slf4j
@Data
public class WebSocketServer {

    @Autowired
    private WsHandle wsHandle;

    @Autowired
    private WsDataHolder wsDataHolder;

    private static ApplicationContext applicationContext;

    // 连接会话
    private Session session;

    // 连接参数
    private Map<String, String> params = new HashMap<>();

    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;

    /**
     * 保存ws会话
     * 为了保存在线用户信息
     */
    public final static Map<WsTopicKey, List<Session>> SESSIONS = new HashMap<>();

    // 注入容器
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServer.applicationContext = applicationContext;
    }

    // WebSocket 服务器端点创建时，通过重写getEndpointInstance获取WebSocketServer实例方法，
    // 当WebSocket框架创建WebSocketServer时，告诉他使用已经由spring创建管理的WebSocketServer实例。
    public static class SpringConfigurator extends ServerEndpointConfig.Configurator {
        @Override
        public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
            log.info("ws实例获取：{}", clazz);
            return applicationContext.getBean(clazz);
        }
    }


    /**
     * 建立连接
     * ws://192.168.31.47:9988/webSocket/{topic}
     * 当客户端发送： ws://192.168.31.47:9988/webSocket/upload ws请求，就可以和这个ws服务端建立ws连接了。
     *
     * @param session ws连接会话
     */
    @OnOpen
    public void onOpen(Session session) {
        String queryString = session.getQueryString();
        String topic = CommonUtils.getParameter(queryString, "topic");
        Long userId = null;

        WsTopicKey topicKey = new WsTopicKey(topic, userId);
        if (SESSIONS.get(topicKey) == null) {
            List<Session> sessions = new ArrayList<>();
            sessions.add(session);
            SESSIONS.put(topicKey, sessions);
        } else
            SESSIONS.get(topicKey).add(session);
        addOnlineCount();

        log.info("[连接topic:{}] 建立连接, 当前连接数:{}", topic, onlineCount);

        wsHandle.handleOpen(session);
    }


    /**
     * 断开连接
     */
    @OnClose
    public void onClose(Session session) {
        String queryString = session.getQueryString();
        String topic = CommonUtils.getParameter(queryString, "topic");
        Long userId = null;

        WsTopicKey topicKey = new WsTopicKey(topic, userId);
        SESSIONS.get(topicKey).remove(session);
        subOnlineCount();

        log.info("[连接topic:{}] 断开连接, 当前连接数:{}", topic, onlineCount);
        wsHandle.handleClose(topicKey);
    }

    /**
     * 收到二进制的数据
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(ByteBuffer message, Session session) {
        String topic = CommonUtils.getParameter(session.getQueryString(), "topic");

        wsHandle.handleByteMessage(message, topic);
    }

    /**
     * 获取topic订阅的在线人数
     *
     * @param topicKey
     * @return
     */
    public int getSessionOnlineCount(WsTopicKey topicKey) {
        int count = SESSIONS.get(topicKey).size();

        log.info("{}订阅人数：{}", topicKey.getTopic(), count);
        return count;
    }

    /**
     * 发送错误
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        String topic = CommonUtils.getParameter(session.getQueryString(), "topic");
        log.info("[连接topic:{}] 错误原因:{}", topic, error.getMessage());
        error.printStackTrace();
        wsHandle.handleError(topic);
    }




    /**
     * 服务端给客户端发送消息
     *
     * @param message  消息
     * @param topicKey 接收消息的主题（只要订阅这个主题都会收到消息）
     */
    public void sendMessage(String message, WsTopicKey topicKey) {
        // 获取主题对应的会话
        List<Session> sessions = SESSIONS.get(topicKey);

        if (sessions.size() > 0) {
            try {
                for (Session session : sessions) {
                    session.getBasicRemote().sendText(message);

//                    synchronized (this) {
//                        session.getBasicRemote().sendText(message);
//                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("[连接topic:{}] 发送消息失败, 消息:{}", topicKey.getTopic(), message, e);
            }
        }
    }


    /**
     * 获取当前连接数
     *
     * @return
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 当前连接数加一
     */
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    /**
     * 当前连接数减一
     */
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

}